#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <stdint.h>
#include <error.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>



#define INTERFACE_DESCRIPTION_BLOCK     0x00000001
#define PACKET_BLOCK                    0x00000002
#define SIMPLE_PACKET_BLOCK             0x00000003
#define NAME_RESOLUTION_BLOCK           0x00000004
#define INTERFACE_STATISTICS_BLOCK      0x00000005
#define ENHANCED_PACKET_BLOCK           0x00000006
#define SECTION_HEADER_BLOCK            0x0A0D0D0A

#define USAGE   "\nFATAL ERROR: Wrong number of arguments.\n" \
                "Usage: %s <input_filename> <hex_search_string>\n", argv[0]


int main(int argc, char *argv[])
{
    char *input_filename;
    FILE *input_filep;
    struct block_header_str {
        uint32_t type;
        uint32_t length;
    } block_header;
    char block_body[BUFSIZ];
    char *block_bodyp;
    char *hex_search_stringp;
    char bin_search_string[BUFSIZ];
    size_t ss_len;
    size_t bytes_read;
    uint32_t i;
    uint32_t CapLen;
    uint64_t packet_number;
    uint32_t body_len;
    char hex_byte[3];


    if (argc != 3)
        error_at_line(EXIT_FAILURE, 0, __FILE__, __LINE__, USAGE);

    input_filename = argv[1];
    hex_search_stringp = argv[2];

    ss_len = strlen(hex_search_stringp);

    if ((ss_len % 2) != 0)
        error_at_line(EXIT_FAILURE, 0, __FILE__, __LINE__,
                "\nFATAL ERROR: search string "
                "must have an even number of digits.\n");

    for (i = 0; i < ss_len; i++)
        if (!isxdigit(*(hex_search_stringp + i)))
            error_at_line(EXIT_FAILURE, 0, __FILE__, __LINE__,
                    "\nFATAL ERROR: search string "
                    "contains a non hexadecimal digit.\n");

    if (ss_len > BUFSIZ)
        error_at_line(EXIT_FAILURE, 0, __FILE__, __LINE__,
                "\nFATAL ERROR: search string is too long.\n"
                "(Max size: %u)", BUFSIZ * 2);

    i = 0;
    hex_byte[2] = '\0';
    while (i < strlen(hex_search_stringp))
    {
        hex_byte[0] = *(hex_search_stringp + i++);
        hex_byte[1] = *(hex_search_stringp + i++);
        bin_search_string[i/2 - 1] = strtol(hex_byte, NULL, 16);
    }

    if ((input_filep = fopen(input_filename, "r")) == NULL)
        error_at_line(EXIT_FAILURE, errno, __FILE__, __LINE__,
                "\nFATAL ERROR: %s", input_filename);

    packet_number = 0;
    while (!feof(input_filep)) {

        if ((bytes_read = (fread(&block_header, 1, sizeof(block_header),
                    input_filep))) != sizeof(block_header)) {
            if ((bytes_read == 0) && (feof(input_filep)))
                continue;
            else if (ferror(input_filep))
                error_at_line(EXIT_FAILURE, 0, __FILE__, __LINE__,
                        "\nFATAL ERROR: Error on reading: %s.",
                        input_filename);
            else
                error_at_line(EXIT_FAILURE, 0, __FILE__, __LINE__,
                        "INVALID FILE FORMAT: Block header not found.");
        }

        body_len = block_header.length - sizeof(block_header);

        if (body_len > sizeof(block_body)) {
            if ((block_bodyp = malloc((size_t) body_len)) == NULL)
                error_at_line(EXIT_FAILURE, 0, __FILE__, __LINE__,
                        "\nFATAL ERROR: Not enough memory.");
        } else
            block_bodyp = block_body;

        if (fread(block_bodyp, 1, body_len, input_filep) != body_len) {
            if (feof(input_filep))
                error_at_line(EXIT_FAILURE, 0, __FILE__, __LINE__,
                        "INVALID FILE FORMAT: Unexpected end of file.");
            else if (ferror(input_filep))
                error_at_line(EXIT_FAILURE, 0, __FILE__, __LINE__,
                        "\nFATAL ERROR: Error on reading: %s.", 
                        input_filename);
            else
                error_at_line(EXIT_FAILURE, 0, __FILE__, __LINE__,
                        "\nFATAL ERROR: Unknown read error: %s.",
                        input_filename);
        }

        switch (block_header.type) {
            case INTERFACE_DESCRIPTION_BLOCK:
                break;

            case PACKET_BLOCK:
                break;

            case SIMPLE_PACKET_BLOCK:
                break;

            case NAME_RESOLUTION_BLOCK:
                break;

            case INTERFACE_STATISTICS_BLOCK:
                break;

            case ENHANCED_PACKET_BLOCK:
                packet_number++;
                CapLen = *((uint32_t *) (block_bodyp + 12));

                for (i = 20; i < 20 + CapLen; i++) {
                    if (bcmp(block_bodyp + i, bin_search_string,
                                ss_len / 2) == 0) {
                        printf("Packet number %lu matches.\n", packet_number);
                        break;
                    }

                }

                break;

            case SECTION_HEADER_BLOCK:
                break;

            default:
                fprintf(stderr, "WARNING: Unknown Header type found.\n");

                break;
        }

    }

    exit(EXIT_SUCCESS);
}
